#pragma once

#include <iostream>
#include <string>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <cstdlib>
#include <unistd.h>
#include <functional>
#include "Log.hpp"
#include "Common.hpp"
#include "InetAddr.hpp"

const static int defaultbacklog = 5;

using namespace LogModule;
class Socket
{
public:
    virtual ~Socket() {}
    virtual void CreateSocket() = 0;
    virtual void BindSocket(uint16_t port) = 0;
    virtual void ListenSocket(int backlog) = 0;
    // virtual void Accept() = 0;
    virtual std::shared_ptr<Socket> Accept(InetAddr *client) = 0;
    virtual void Close() = 0;
    virtual int Recv(std::string *out) = 0;
    virtual int Send(const std::string &message) = 0;
    virtual int Connect(const std::string &server_ip, uint16_t port) = 0;

    void BuildTcpConnectionMethod(uint16_t port, int backlog = defaultbacklog)
    {
        CreateSocket();
        BindSocket(port);
        ListenSocket(backlog);
    }
};

const static int defaultfd = -1;

class TcpSocket : public Socket
{
public:
    TcpSocket(int sockfd = defaultfd) : _sockfd(sockfd)
    {
    }
    ~TcpSocket() {}
    void CreateSocket()
    {
        _sockfd = ::socket(AF_INET, SOCK_STREAM, 0);
        if (_sockfd < 0)
        {
            LOG(LogLevel::FATAL) << "socket error";
            exit(SOCKET_ERR);
        }
        LOG(LogLevel::INFO) << "socket success";
    }
    void BindSocket(uint16_t port)
    {
        InetAddr peer(port);
        int n = ::bind(_sockfd, peer.GetAddr(), peer.GetAddrLen());
        if (n < 0)
        {
            LOG(LogLevel::FATAL) << "bind error";
            exit(BIND_ERR);
        }
        LOG(LogLevel::INFO) << "bind success";
    }
    void ListenSocket(int backlog)
    {
        int n = ::listen(_sockfd, backlog);
        if (n < 0)
        {
            LOG(LogLevel::FATAL) << "listen error";
            exit(LISTEN_ERR);
        }
        LOG(LogLevel::INFO) << "listen success";
    }
    std::shared_ptr<Socket> Accept(InetAddr *client)
    {
        struct sockaddr_in peer;
        socklen_t len = sizeof(peer);//一定要设置大小
        int fd = ::accept(_sockfd, CONV(peer), &len);
        if (fd < 0)
        {
            LOG(LogLevel::WARING) << "accept error...";
            return nullptr;
        }
        client->SetAddr(peer);
        return std::make_shared<TcpSocket>(fd);
    }
    void Close()
    {
        if (_sockfd >= 0)
        {
            ::close(_sockfd);
        }
    }
    int Recv(std::string *out)
    {
        char buffer[4096 * 2];
        int n = recv(_sockfd, buffer, sizeof(buffer) - 1, 0);
        if (n > 0)
        {
            buffer[n] = 0;
            *out += buffer;
        }
        return n;
    }
    int Send(const std::string &message)
    {
        send(_sockfd, message.c_str(), message.size(), 0);
    }
    int Connect(const std::string &server_ip, uint16_t port)
    {
        InetAddr server(server_ip, port);
        return ::connect(_sockfd, server.GetAddr(), server.GetAddrLen());
    }

private:
    int _sockfd;
};